/*
 * fake_hd.c
 *
 *  Created on: 2016-11-14
 *      Author: m
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define dprintf(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)

int w_dot_file(char* fn_dot, char* buf, int len) {
    FILE* f = 0;
    f = fopen(fn_dot, "wb");
    if(!f) {
        dprintf("open %s failed!\n", fn_dot);
        return -1;
    }

    if(1 != fwrite(buf, len, 1, f)) {
        dprintf("read %s failed!\n", fn_dot);
        fclose(f);
        return -1;
    }
    fclose(f);
}

static int w_pos = 0;
static int buf_len = 0;
static char* dot = 0;
int add_dot_buf(char* str) {
    int l = strlen(str);
    if(w_pos + l < buf_len) {
        memcpy(&dot[w_pos], str, l);
        w_pos += l;
        return 0;
    }
    return -1;
}

int read_data_from_hd(char* hd_img, int lba, int offset, char* buf, int len) {
    FILE* f = 0;
    //dprintf("lba=0x%x(%d)\n", lba*512, lba*512);
    f = fopen(hd_img, "rb");
    if(!f) {
        dprintf("open %s failed!\n", hd_img);
        return -1;
    }

    fseek(f, lba * 512 + offset, SEEK_SET);
    if(1 != fread(buf, len, 1, f)) {
        dprintf("read %s failed!\n", hd_img);
        fclose(f);
        return -1;
    }
    fclose(f);
    return 0;
}

//# 分区表结构
//  偏移    长度    描述
//  0       1      状态(0x80:可引导 0x00:不可引导,其它非法)
//  1       1      起始磁头号
//  2       1      起始扇区号(仅用了低6位，高2位为起始柱面号的第8,9位)
//  3       1      起始柱面号低8位
//  4       1      分区类型
//  5       1      结束磁头号
//  6       1      结束扇区号(仅用了低6位，高2位为结束柱面号的第8，9位)
//  7       1      结束柱面号的低8位
//  8       4      起始扇区的LBA
//  12      4      扇区数目
typedef struct {
    unsigned char state;
    unsigned char start_header_no;
    unsigned char start_sect_no;
    unsigned char start_cyl_no;
    unsigned char part_type;
    unsigned char end_header_no;
    unsigned char end_sect_no;
    unsigned char end_cyl_no;
    unsigned int begin_lba;
    unsigned int sect_num;
} __attribute__((packed)) PART_DESC;

static int e_base = 0;
char dbuf[512] = {0};
void part_read(char* hd_img, int lba) {
    char buf[16 * 4] = {0};
    static int deep = -1;
    int l2 = 0;
    deep++;
    read_data_from_hd(hd_img, lba, 0x1BE, buf, sizeof(buf));
    PART_DESC* p = (PART_DESC*)buf;
    for(int i = 0; i < 4; ++i) {
        if(p[i].part_type == 0) {
            continue;
        }

        if(0 == e_base || 0x05 != p[i].part_type) {
            printf("0x%02X   0x%02X   0x%08X(%d) 0x%08X(%d) %0.1fM\n", p[i].state & 0xFF,
                    p[i].part_type & 0xFF, p[i].begin_lba + lba, lba + p[i].begin_lba, p[i].sect_num,
                    p[i].sect_num, p[i].sect_num * 512.0 / 1024 / 1024);
        }

        memset(dbuf, 0, sizeof(dbuf));
        sprintf(dbuf, "    part_desc%d_%d [label=\"{<head>part_desc%d_%d|type=0x%x|start_lba=0x%x|sect_num=0x%x}\"];\n",deep, i, deep, i, p[i].part_type & 0xFF, p[i].begin_lba, p[i].sect_num);
        add_dot_buf(dbuf);

        if(0x05 == p[i].part_type) { //extention part
            l2 = e_base + p[i].begin_lba;
            if(0 == e_base) {
                e_base = p[i].begin_lba;
            }
            part_read(hd_img, l2);
        }
    }
}

void fdisk_list(char* hd_img) {
    char buf[512] = {0};
    dot = (char*)malloc(1024 * 1024);
    buf_len = 1024 * 1024;
    add_dot_buf("digraph test2 {\n");
    add_dot_buf("fontname = \"Verdana\";\n");
    add_dot_buf("fontsize = 10;\n");
    add_dot_buf("rankdir=TB;\n");
    add_dot_buf("node [fontname = \"Verdana\", fontsize = 10, color=\"skyblue\", shape=\"record\"];\n");
    add_dot_buf("edge [fontname = \"Verdana\", fontsize = 10, color=\"crimson\", style=\"solid\"];\n");

    //add_dot_buf("st_table:type -> st_hash_type:head;\n");
    //add_dot_buf("st_table_entry:next -> st_table_entry:head [style=\"dashed\", color=\"forestgreen\"];\n");
    add_dot_buf("\n");

    printf("state part_type begin_lba       sect_num\n");
    part_read(hd_img, 0);
    e_base = 0;

    add_dot_buf("}\n");
    w_dot_file("./memo/hd.dot", dot, w_pos);
}

